import { ipcMain } from 'electron'
import fs from 'fs'
import path from 'path'
import dayjs from 'dayjs'
import { isEmpty } from 'lodash'
import {
  count,
  insert,
  remove as deleteModel,
  selectByID,
  selectPage,
  listModel,
  listModel2,
  selectByIDCommonModel,
  countCommon,
  updateDigitalHuman,
  deleteHuman
} from '../dao/f2f-model.js'
import { train as trainVoice } from './voice.js'
import { assetPath, ensureConfigInitialized, serviceUrl } from '../config/config.js'
import log from '../logger.js'
import { extractAudio } from '../util/ffmpeg.js'
import { uploadModelVideo, uploadAudio } from '../util/upload.js'
import os from 'os'

const MODEL_NAME = 'model'

// 检查路径是否为HTTP URL
function isHttpUrl(path) {
  return path && (path.startsWith('http://') || path.startsWith('https://'));
}

/**
 * 新增模特
 * @param {string} modelName 模特名称
 * @param {string} videoPath 模特视频路径
 * @param synced
 * @param primaryCategoryId
 * @param secondaryCategoryId
 * @param tertiaryCategoryId
 * @param lang
 * @returns
 */
async function addModel(modelName, videoPath, synced, primaryCategoryId, secondaryCategoryId, tertiaryCategoryId, lang) {
  try {
    // 确保配置初始化完成
    await ensureConfigInitialized();

    const http_url = serviceUrl.baseUrlWithPort
    // 创建临时工作目录
    const tempDir = path.join(os.tmpdir(), 'heygem_temp');
    if (!fs.existsSync(tempDir)) {
      fs.mkdirSync(tempDir, { recursive: true });
    }

    // 生成模特文件名
    const extname = path.extname(videoPath);
    const modelFileName = dayjs().format('YYYYMMDDHHmmssSSS') + extname;
    const audioFileName = modelFileName.replace(extname, '.wav');

    // 创建临时视频和音频文件路径
    const tempVideoPath = path.join(tempDir, modelFileName);
    const tempAudioPath = path.join(tempDir, audioFileName);

    // 复制视频到临时目录
    fs.copyFileSync(videoPath, tempVideoPath);

    // 用ffmpeg分离音频到临时目录
    await extractAudio(tempVideoPath, tempAudioPath);

    // 定义视频和音频的相对路径（用于数据库存储）
    const relativeModelPath = modelFileName;
    let relativeModelImage;
    const relativeAudioPath = 'origin_audio\\' + audioFileName;

    // 处理模型视频和音频文件
    if (isHttpUrl(assetPath.model)) {
      // 如果是HTTP URL，上传视频和音频到服务器
      log.debug('上传视频文件到服务器')
      let { videoPath, thumbnailUrl } = await uploadModelVideo(tempVideoPath, modelFileName, http_url)
      relativeModelImage = thumbnailUrl
      log.debug('上传音频文件到服务器')
      await uploadAudio(tempAudioPath, audioFileName, http_url)
    } else {
      // 如果是本地路径，创建目录并复制文件
      if (!fs.existsSync(assetPath.model)) {
        fs.mkdirSync(assetPath.model, { recursive: true });
      }

      const modelDestPath = path.join(assetPath.model, modelFileName);
      fs.copyFileSync(tempVideoPath, modelDestPath);

      // 确保TTS训练目录存在
      const ttsTrainDir = path.join(assetPath.ttsRoot, 'origin_audio');
      if (!fs.existsSync(ttsTrainDir)) {
        fs.mkdirSync(ttsTrainDir, { recursive: true });
      }

      const audioDestPath = path.join(ttsTrainDir, audioFileName);
      fs.copyFileSync(tempAudioPath, audioDestPath);
    }

    // 设置训练的相对路径
    const trainPath = 'origin_audio/' + audioFileName;

    // 训练语音模型
    log.debug('开始训练语音模型，训练路径:', trainPath);
    const voiceId = await trainVoice(trainPath, lang, synced);
    log.debug('语音模型训练完成，voiceId:', voiceId);

    if (synced) {
      // 使用 fetch API 将模型同步到服务器
      const response = await fetch(`${http_url}/apis/model/api/model/add`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          modelName,
          videoPath: relativeModelPath,
          audioPath: relativeAudioPath,
          imagePath: relativeModelImage,
          voiceId,
          primaryCategoryId,
          secondaryCategoryId,
          tertiaryCategoryId,
          language: lang,
        })
      });

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      const result = await response.json();
      log.debug('模型已同步到服务器，响应:', JSON.stringify(result));
      return result.data.id;
    } else {
      // 插入本地数据库
      const id = insert({
        modelName,
        videoPath: relativeModelPath,
        audioPath: relativeAudioPath,
        voiceId
      });

      log.debug('模型已添加到本地数据库，id:', id);
      return id;
    }
  } catch (error) {
    log.error('添加模型失败:', error);
    throw error;
  }
}

// 查询模特列表
function page({ page, pageSize, name = '' }) {
  const total = count(name);
  return {
    total,
    list: selectPage({ page, pageSize, name }).map((model) => {
      // 构建URL或路径
      const videoPath = isHttpUrl(assetPath.model)
        ? `${assetPath.model}/${model.video_path}`
        : path.join(assetPath.model, model.video_path);

      const audioPath = isHttpUrl(assetPath.ttsRoot)
        ? `${assetPath.ttsRoot}/${model.audio_path}`
        : path.join(assetPath.ttsRoot, model.audio_path);

      return {
        ...model,
        video_path: videoPath,
        audio_path: audioPath
      };
    })
  };
}

// 查找模特
function findModel(modelId) {
  const model = selectByID(modelId);

  // 构建URL或路径
  const videoPath = isHttpUrl(assetPath.model)
    ? `${assetPath.model}/${model.video_path}`
    : path.join(assetPath.model, model.video_path);

  const audioPath = isHttpUrl(assetPath.ttsRoot)
    ? `${assetPath.ttsRoot}/${model.audio_path}`
    : path.join(assetPath.ttsRoot, model.audio_path);

  return {
    ...model,
    video_path: videoPath,
    audio_path: audioPath
  };
}

// 查找公共模特
async function findModelCommon(modelId) {
  const model = await selectByIDCommonModel(modelId);

  // 构建URL或路径
  const videoPath = isHttpUrl(assetPath.model)
    ? `${assetPath.model}/${model.videoPath}`
    : path.join(assetPath.model, model.videoPath);

  const audioPath = isHttpUrl(assetPath.ttsRoot)
    ? `${assetPath.ttsRoot}/${model.audioPath}`
    : path.join(assetPath.ttsRoot, model.audioPath);

  return {
    ...model,
    video_path: videoPath,
    audio_path: audioPath
  };
}

// 删除模特
async function removeModel(modelId) {
  const model = selectByID(modelId);
  log.debug('~ removeModel ~ modelId:', modelId);

  // 如果路径是HTTP URL，不尝试删除文件
  if (!isHttpUrl(assetPath.model) && !isEmpty(model.video_path)) {
    const videoPath = path.join(assetPath.model, model.video_path);
    if (fs.existsSync(videoPath)) {
      fs.unlinkSync(videoPath);
    }
  }

  if (!isHttpUrl(assetPath.ttsRoot) && !isEmpty(model.audio_path)) {
    const audioPath = path.join(assetPath.ttsRoot, model.audio_path);
    if (fs.existsSync(audioPath)) {
      fs.unlinkSync(audioPath);
    }
  }

  deleteModel(modelId);
}

function countModel(name = '') {
  return count(name);
}

function updateModel(modelData) {
  return updateDigitalHuman(modelData);
}

function deleteModelById(modelId) {
  return deleteHuman(modelId);
}

function countModelCommon(name = '') {
  return countCommon(name);
}

export function init() {
  ipcMain.handle(MODEL_NAME + '/addModel', async (event, ...args) => {
    return await addModel(...args);
  });

  ipcMain.handle(MODEL_NAME + '/updateModel', async (event, ...args) => {
    return await updateModel(...args);
  });

  ipcMain.handle(MODEL_NAME + '/deleteModel', async (event, ...args) => {
    return await deleteModelById(...args);
  });

  ipcMain.handle(MODEL_NAME + '/page', (event, ...args) => {
    return page(...args);
  });

  ipcMain.handle(MODEL_NAME + '/find', (event, ...args) => {
    return findModel(...args);
  });

  ipcMain.handle(MODEL_NAME + '/find_common', async (event, ...args) => {
    return await findModelCommon(...args);
  });

  ipcMain.handle(MODEL_NAME + '/count', (event, ...args) => {
    return countModel(...args);
  });

  ipcMain.handle(MODEL_NAME + '/count_common', (event, ...args) => {
    return countModelCommon(...args);
  });

  ipcMain.handle(MODEL_NAME + '/remove', async (event, ...args) => {
    return await removeModel(...args);
  });

  ipcMain.handle(MODEL_NAME + '/list', (event, ...args) => {
    return listModel(...args);
  });

  ipcMain.handle(MODEL_NAME + '/list2', (event, ...args) => {
    return listModel2(...args);
  });
}
